/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package wave;
import base.*;
import base.TrackNode.Echo;
import base.TrackNode.Rule;
import base.TrackNode.Type;
import base.WaveEnvironment.Mode;
import communicator.message.MsgCreateTrack;
import communicator.message.MsgEcho;
import communicator.message.MsgTail;
import java.util.ArrayList;
import trackmsg.ActivateMsg;
import trackmsg.CreateMsg;
import trackmsg.EchoMsg;
import trackmsg.ExpandRemoteTrack;
import trackmsg.ExpandhMsg;
import trackmsg.ExpandsMsg;
import trackmsg.TailMsg;
import trackmsg.TrackCtrlMsg;
/**
 *
 * @author Tue
 */
public class TrackProcessor extends Thread{
    private SynchronousQueue trackQueue;
    private WaveInterpreter wi;
    private TrackForest trackForest;
    public TrackProcessor(WaveInterpreter wi){
        this.wi = wi;
        trackQueue = wi.getTrackQueue();
        trackForest = wi.getTrackForest();
    }
    @Override
    public void run(){
        Utilities.TPPrint("running...");
        while(true){
            Object obj = trackQueue.dequeue();
            if(obj instanceof TrackCtrlMsg){
                TrackCtrlMsg msg = (TrackCtrlMsg) obj;
                Utilities.TPPrint("#Recv: " + msg.toString());
                switch(msg.getType()){
                    case CREATE:
                        processCreateMsg((CreateMsg)msg);
                        break;
                    case EXPANDH:
                        processExpandhMsg((ExpandhMsg)msg);
                        break;
                    case EXPANDREMOTE:
                        processExpandRemote((ExpandRemoteTrack) msg);
                        break;
                    case EXPANDS:
                        processExpandsMsg((ExpandsMsg)msg);
                        break;
                    case ACTIVATE:
                        processActivateMsg((ActivateMsg)msg);
                        break;
                    case ECHO:
                        processEchoMsg((EchoMsg)msg);
                        break;
                    case TAIL:
                        processTailMsg((TailMsg)msg);
                        break;
                }
            }
           // Utilities.delay(1000);
        }
    }    
    private void processCreateMsg(CreateMsg msg){        
        String enclosedWave = msg.getEnclosedWave().equals("")?null:msg.getEnclosedWave();
        String tail = msg.getTail().equals("")?null:msg.getTail();
        WaveEnvironment env = msg.getWaveEnv();
        TrackNode parent = trackForest.getTrackNode("" + env.getTrackAddress());
        TrackNode trackNode = trackForest.createTrack(enclosedWave, tail, env);
        trackNode.setParent(parent);
        trackNode.setRule(msg.getRule()); //Assign rule
        if(msg.getRule() == Rule.CR){
            env.setWaveMode(Mode.CREATE);
        }
        //Gui wave di
        sendWave(enclosedWave, env);
    }

    private void processExpandRemote(ExpandRemoteTrack msg) {
        TrackNode currentTrack = msg.getCurrentTrack();//Lay ra track hien tai
        //tao track moi
        TrackNode newTrack = trackForest.createTrack(null, msg.getWave(), msg.getWaveEnv());
        newTrack.setType(Type.SECT);
        newTrack.setHost(msg.getHost());
        currentTrack.suspended(newTrack);
        newTrack.setRule(Rule.NM);
        Utilities.TPPrint("EXPANDREMOTE DONE! " + newTrack);
    }
    
    private void processExpandsMsg(ExpandsMsg msg){
        TrackNode currentTrack = msg.getCurrentTrack();//Lay ra track hien tai
        //tao track moi
        TrackNode newTrack = trackForest.createTrack(null, msg.getWave(), msg.getWaveEnv());
        newTrack.setType(Type.SECT);
        currentTrack.suspended(newTrack);
        //if(currentTrack.getRule() == Rule.RP)
            newTrack.setRule(Rule.NM);
        //else if(currentTrack.getType() == Type.SECT) newTrack.setRule(Rule.NM);
        //else newTrack.setRule(currentTrack.getRule());//Gan rule cua node cha cho node hien tai
        Utilities.TPPrint("EXPANDS DONE!");
    }
    private void processExpandhMsg(ExpandhMsg msg){
        TrackNode currentTrack = msg.getCurrentTrack();//Lay ra track hien tai
        //tao track moi
        TrackNode newTrack = trackForest.createTrack("", msg.getWave(), msg.getWaveEnv());
        currentTrack.suspended(newTrack);
        newTrack.setRule(TrackNode.Rule.NM); //RULE cua node moi la Normal
        Utilities.TPPrint("EXPANDH DONE!");
    }
    private void processActivateMsg(ActivateMsg msg){
        TrackNode currentTrack = msg.getCurrentTrack();        
        switch(currentTrack.getRule()){
            case SQ:
            case AS:
            case OS:
                if(currentTrack.hasSuspendedBranch()){
                    TrackNode branch = currentTrack.nextBranch();
                    currentTrack.addChild(branch);
                    if(branch.isRemoteTrack()){
                        Utilities.TPPrint("Send track to " + branch.getHost());
                        MsgCreateTrack msgCreateTrack = new MsgCreateTrack(branch.getType(),
                                branch.getParent().getAddress(), branch.getAddress(),
                                branch.getRule(), branch.getEnclosedWave(),
                                branch.getSuspendedTail(), branch.getWaveEnv());
                        msgCreateTrack.setDestinationComputer(branch.getHost());
                        wi.getPropagationQueue().incoming(msgCreateTrack);
                        Utilities.TPPrint("abc" + msgCreateTrack.getTrackAddress());
                    } else {
                        String waveString = branch.getSuspendedTail();
                        if(waveString != null){
                            branch.suspendedTail(null);
                            sendWave(waveString, branch.getWaveEnv());
                        }
                    }
                }
                break;
            case NM:
            case CR:
            case RP:
            case WT:
            case ID:
            case OP:
            case AP:
                while(currentTrack.hasSuspendedBranch()){
                    TrackNode branch = currentTrack.nextBranch();
                    currentTrack.addChild(branch);
                    if(branch.isRemoteTrack()){
                        Utilities.TPPrint("Send track to " + branch.getHost());
                        MsgCreateTrack msgCreateTrack = new MsgCreateTrack(branch.getType(),
                                branch.getParent().getAddress(), branch.getAddress(),
                                branch.getRule(), branch.getEnclosedWave(),
                                branch.getSuspendedTail(), branch.getWaveEnv());
                        msgCreateTrack.setDestinationComputer(branch.getHost());
                        wi.getPropagationQueue().incoming(msgCreateTrack);
                        Utilities.TPPrint("abc" + msgCreateTrack.getTrackAddress());
                    } else {
                        String waveString = branch.getSuspendedTail();
                        if(waveString != null){
                            branch.suspendedTail(null);
                            sendWave(waveString, branch.getWaveEnv());
                        }
                    }
                }
                break;
            case RD:
                if(currentTrack.hasSuspendedBranch()){
                    TrackNode branch = currentTrack.nextRandomBranch();
                    currentTrack.addChild(branch);
                    if(branch.isRemoteTrack()){
                        Utilities.TPPrint("Send track to " + branch.getHost());
                        MsgCreateTrack msgCreateTrack = new MsgCreateTrack(branch.getType(),
                                branch.getParent().getAddress(), branch.getAddress(),
                                branch.getRule(), branch.getEnclosedWave(),
                                branch.getSuspendedTail(), branch.getWaveEnv());
                        msgCreateTrack.setDestinationComputer(branch.getHost());
                        wi.getPropagationQueue().incoming(msgCreateTrack);
                    } else {
                        String waveString = branch.getSuspendedTail();
                        if(waveString != null){
                            branch.suspendedTail(null);
                            sendWave(waveString, branch.getWaveEnv());
                        }
                    }
                }
                break;
        }
    }
    private void processEchoMsg(EchoMsg msg){
        if(msg.getFromTrack() == null) return;
        Utilities.TPPrint("PROCESSING ECHO FROM " + msg.getFromTrack() + 
                " TO " + msg.getFromTrack().getParent());
        Echo echo = msg.getEcho();
        TrackNode fromTrack = msg.getFromTrack();
        boolean keep = false;
        /*
         * Neu track gui echo la fingerTrack thi se merge echo cho finger track do
         */
        if(msg.getWaveEnv() != null && fromTrack.isFingerTrack()) {
            fromTrack.setWaveEnv(msg.getWaveEnv());
        }
        
        if(fromTrack.getRule() != Rule.NM && fromTrack.isFingerTrack()){
            fromTrack.mergeEcho(msg.getEcho());
            if(fromTrack.getEcho() == Echo.TRUE){
                    if(fromTrack.getRule() == Rule.RP){
                        String waveString = null;
                        if(fromTrack.getEnclosedWave() != null)
                            waveString = "RP(" + fromTrack.getEnclosedWave() + ")";
                        if(fromTrack.getSuspendedTail() != null)
                            waveString += "."+ fromTrack.getSuspendedTail();
                        fromTrack.setEnclosedWave(null);
                        fromTrack.suspendedTail(null);
                        if(waveString != null){
                            WaveEnvironment waveEnv = fromTrack.getWaveEnv().clone();
                            if(fromTrack.getParent() != null){
                                TrackNode parent = fromTrack.getParent();
                                parent.decreaseRemainingEcho();
                                waveEnv.setTrackAddress(parent.getAddress());
                            } else waveEnv.setTrackAddress(null);
                            wi.getTrackForest().removeTrack(fromTrack.getAddress());
                            sendWave(waveString, waveEnv);
                        } else {
                            fromTrack.setRule(Rule.NM);
                            sendEcho(fromTrack, Echo.TRUE, fromTrack.getWaveEnv());
                        }
                    } else {
                        fromTrack.setRule(Rule.NM);
                        if(fromTrack.getParent() != null) keep = true;
                        if(fromTrack.getSuspendedTail() != null)
                            sendTail(fromTrack, fromTrack.getSuspendedTail(), keep);
                        else {
                            sendEcho(fromTrack, fromTrack.getEcho(), fromTrack.getWaveEnv());
                        }
                    }
            } else {
                if(fromTrack.getRule() == Rule.RP){
                    if (fromTrack.getSuspendedTail() != null){
                        WaveEnvironment newEnv = fromTrack.getWaveEnv();                        
                        String waveTail = fromTrack.getSuspendedTail();
                        if(fromTrack.getParent() != null){
                            TrackNode parent = fromTrack.getParent();
                            parent.decreaseRemainingEcho();
                            newEnv.setTrackAddress(parent.getAddress());
                        }
                        else newEnv.setTrackAddress(null);
                        wi.getTrackForest().removeTrack(fromTrack.getAddress());
                        sendWave(waveTail, newEnv);
                    } else {
                        fromTrack.setRule(Rule.NM);
                        sendEcho(fromTrack, Echo.TRUE, fromTrack.getWaveEnv());
                    }
                } else {
                    fromTrack.setRule(Rule.NM);
                    sendEcho(fromTrack, fromTrack.getEcho(), fromTrack.getWaveEnv());
                }
            }            
            return;
        }

        TrackNode currentTrack = fromTrack.getParent();
        if(currentTrack == null) {
            Utilities.TPPrint("Track destroy!!!!");
            return;
        }
        currentTrack.getWaveEnv().getFrontalTable().clear();
        currentTrack.getWaveEnv().getFrontalTable().putAll(fromTrack.getWaveEnv().getFrontalTable());
        if( currentTrack.isRemoteTrack()){
            //TODO gui Echo message sang may khac
            MsgEcho msgEcho = new MsgEcho(fromTrack.getAddress(), currentTrack.getAddress(), msg.getEcho(), msg.getWaveEnv());
            msgEcho.setDestinationComputer(currentTrack.getHost());
            wi.getPropagationQueue().incoming(msgEcho);
            return;
        }     
        
       
        if(currentTrack.getParent() != null) keep = true;
        currentTrack.mergeEcho(echo);
        currentTrack.decreaseRemainingEcho();
        int remainingEcho = currentTrack.getRemainingEcho();
        String tail = currentTrack.getSuspendedTail();
        if(echo == Echo.FALSE || echo == Echo.DONE || echo == Echo.ABORT){
             //currentTrack.deleteBranch(fromTrack);
            wi.getTrackForest().removeTrack(fromTrack.getAddress());
        }
        Utilities.TPPrint("Remaining Echo = " + remainingEcho + " of " + currentTrack);
        switch( currentTrack.getRule()){
            case CR:
            case NM:
            case RD:
                if( remainingEcho == 0){//Neu nhan du echo tu cac nhanh con
                    if( tail != null){//Send tail toi tat ca nhanh con di neu co tail
                        for(int i = 0; i< currentTrack.getNumberChild(); i++){
                            if(keep){
                                currentTrack.increaseRemainEcho();
                                currentTrack.setEcho(Echo.NONE);
                            }
                            currentTrack.suspendedTail(null);
                            sendTail(currentTrack.getChild(i),
                                    tail, keep);
                        }
//                       currentTrack.setCounter(currentTrack.getNumberChild());
                    } else { //Neu khong co tail thi send echo len tren neu parent != null
                        //send echo neu parent cua parent != null
                        if( currentTrack.getParent() != null){
                            sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                        } else {
                            Utilities.TPPrint("Track destroy");
                        }
                    }
                }
                break;
            case SQ:
                if (!currentTrack.hasSuspendedBranch()) {
                    if(tail == null ||
                        currentTrack.getEcho() == Echo.FALSE || currentTrack.getEcho() == Echo.DONE || currentTrack.getEcho() == Echo.ABORT){
                        if(currentTrack.getParent() != null)
                            sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                    } else {
                        for(int i = 0; i< currentTrack.getNumberChild(); i++){
                            if(keep) {
                                currentTrack.increaseRemainEcho();
                                currentTrack.setEcho(Echo.NONE);
                            }
                            currentTrack.suspendedTail(null);
                            sendTail(currentTrack.getChild(i),
                                    tail, keep);
                        }
                    }
                } else {
                    activateNextBranchOf(currentTrack);
                }
                break;

            case OS:
                if(msg.getEcho() == Echo.TRUE){
                    if(tail != null){
                        if(keep) {
                            currentTrack.increaseRemainEcho();
                            currentTrack.setEcho(Echo.NONE);
                        }
                        currentTrack.suspendedTail(null);
                        sendTail(fromTrack, tail, keep);
                    }
                    else if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                } else if(!currentTrack.hasSuspendedBranch() || currentTrack.getEcho() == Echo.ABORT){
                    if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                } else 
                    activateNextBranchOf(currentTrack);
                break;
            case OP:                                
                if(msg.getEcho() == Echo.TRUE){
                    if(tail != null){
                        if(keep){
                            currentTrack.increaseRemainEcho();
                            currentTrack.setEcho(Echo.NONE);
                        }
                        currentTrack.suspendedTail(null);
                        sendTail(fromTrack, tail, keep);
                        for(int i = 0; i < currentTrack.getAllChilds().size(); i++){
                            TrackNode trackChild = currentTrack.getChild(i);
                            if(!trackChild.equals(fromTrack))
                                wi.getTrackForest().removeTrack(currentTrack.getChild(i).getAddress());
                        }
                    } else if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                } else if(remainingEcho == 0)
                    if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                break;
            case AS:
                if(echo == Echo.FALSE || echo == Echo.ABORT || echo == Echo.DONE){
                    if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                } else {
                    if (!currentTrack.hasSuspendedBranch()) {
                        if (tail == null) {
                            if(currentTrack.getParent() != null){
                                sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                                //Xoa cac nhanh con lai
                                for(int i = 0; i < currentTrack.getAllChilds().size(); i++){
                                    TrackNode trackChild = currentTrack.getChild(i);
                                    if(!trackChild.equals(fromTrack))
                                        wi.getTrackForest().removeTrack(currentTrack.getChild(i).getAddress());
                                }
                            }
                        } else if(currentTrack.getEcho() == Echo.TRUE){
                            for(int i = 0; i< currentTrack.getNumberChild(); i++){
                                if(keep) {
                                    currentTrack.increaseRemainEcho();
                                    currentTrack.setEcho(Echo.NONE);
                                }
                                currentTrack.suspendedTail(null);
                                sendTail(currentTrack.getChild(i),
                                        tail, keep);
                            }
                        }
                    } else {
                        activateNextBranchOf(currentTrack);
                    }
                }
                break;
            case AP:
                if(echo == Echo.FALSE || echo == Echo.ABORT || echo == Echo.DONE){
                    if(currentTrack.getParent() != null)
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                } else {
                    if (remainingEcho == 0) {
                        if (tail == null) {
                            sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                        } else if(currentTrack.getEcho() == Echo.TRUE){
                            for(int i = 0; i< currentTrack.getNumberChild(); i++){
                                if(keep){
                                    currentTrack.increaseRemainEcho();
                                    currentTrack.setEcho(Echo.NONE);
                                }
                                currentTrack.suspendedTail(null);
                                sendTail(currentTrack.getChild(i),
                                        tail, keep);
                            }
                        }
                    } 
                }
                break;
            case RP:                
                if(echo == Echo.ABORT){
                    
                }
                if(remainingEcho == 0){
                    if(currentTrack.getEcho() == Echo.FALSE || currentTrack.getEcho() == Echo.DONE){
                        if(tail == null){
                            if(currentTrack.getParent() != null){
                                sendEcho(currentTrack, Echo.TRUE, currentTrack.getWaveEnv());
                            }
                        } else {
                            currentTrack.suspendedTail(null);
                            sendWave(tail, currentTrack.getWaveEnv());
                        }
                    } else {
                        String waveString = null;
                        if(currentTrack.getEnclosedWave() != null){
                            waveString = "RP(" + currentTrack.getEnclosedWave() + ")";    
                        }
                        if(currentTrack.getSuspendedTail() != null)
                            waveString += "." + currentTrack.getSuspendedTail();
                        
                        if(waveString != null){
                            for(int i = 0; i< currentTrack.getNumberChild(); i++){
                                if(keep) {
                                    currentTrack.increaseRemainEcho();
                                    currentTrack.setEcho(Echo.NONE);
                                }
                                currentTrack.suspendedTail(null);
                                currentTrack.setRule(Rule.NM);
                                sendTail(currentTrack.getChild(i), waveString, keep);
                            }
                        } else {
                            if(currentTrack.getParent() != null)
                                sendEcho(currentTrack, Echo.TRUE, currentTrack.getWaveEnv());
                        }
                    }
                }
                break;
            case WT:
                currentTrack.mergeEcho(echo);
                currentTrack.decreaseRemainingEcho();

                if(echo.equals(Echo.ABORT)){                    
                    currentTrack.setRule(Rule.NM);
                    
                } else if(echo.equals(Echo.FALSE) || echo == Echo.DONE) {
                    currentTrack.deleteBranch(fromTrack);
                    wi.getTrackForest().removeTrack(fromTrack.getAddress());
                }
                if(currentTrack.getRemainingEcho() == 0){
                    if (currentTrack.getSuspendedTail() == null) {
                        sendEcho(currentTrack, currentTrack.getEcho(), currentTrack.getWaveEnv());
                    
                    } else if(currentTrack.getEcho().equals(Echo.TRUE)) {
                        for(int i = 0; i< currentTrack.getNumberChild(); i++){
                            sendTail(currentTrack.getChild(i),
                                    currentTrack.getSuspendedTail(), keep);
                        }
                    }
                }
            case ID:
                //under construction
        }
    }    
    private void processTailMsg(TailMsg msg){
        Utilities.TPPrint("GET TAIL FROM: " + msg.getToTrack().getParent());
        TrackNode currentTrack = msg.getToTrack();
        if(currentTrack.isRemoteTrack()){
            MsgTail msgTail = new MsgTail(msg.getTail(), currentTrack.getAddress(), msg.isKeep());
            msgTail.setDestinationComputer(currentTrack.getHost());
            wi.getPropagationQueue().incoming(msgTail);
            return;
        }
        ArrayList<TrackNode> children = currentTrack.getAllChilds();
        if(children.size() > 0){
            for(int i = 0; i<children.size(); i++){
                if(msg.isKeep()){
                    currentTrack.increaseRemainEcho();
                    currentTrack.setEcho(Echo.NONE);
                }
                currentTrack.setRule(Rule.NM);
                sendTail(children.get(i), msg.getTail(), msg.isKeep());
            }            
        } else {
            WaveEnvironment waveEnv = currentTrack.getWaveEnv();
            if(!msg.isKeep()) {
                currentTrack.destroy();
                waveEnv.setWaveMode(Mode.NAVIGATE);
                waveEnv.setTrackAddress(null);
            }
            sendWave(msg.getTail(), waveEnv);
        }
    }
    private void activateNextBranchOf(TrackNode track){
        //activate next branch
        ActivateMsg activateMsg = new ActivateMsg(track);
        wi.getTrackQueue().incoming(activateMsg);
    }
    private void sendWave(String waveString, WaveEnvironment waveEnv){
        Wave wave = new Wave(waveString, new WaveEnvironment(waveEnv));
        wi.getWaveQueue().incoming(wave);        
    }
    private void sendTail(TrackNode toTrack, String tail, boolean keep){
        TailMsg tailMsg = new TailMsg(toTrack,  tail, keep);
        wi.getTrackQueue().incoming(tailMsg);
    }
    private void sendEcho(TrackNode fromTrack, Echo echo, WaveEnvironment env){
        fromTrack.setRule(Rule.NM);
        EchoMsg echoMsg = new EchoMsg(fromTrack, echo, env);
        Utilities.TPPrint("#Send Echo: " + echoMsg + " to " + fromTrack.getParent());
        wi.getTrackQueue().incoming(echoMsg);
    }
}
